{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "class Perceptron(object):\n",
    "\n",
    "    def __init__(self, eta: float = 0.01, n_iter: int = 10):\n",
    "        \"\"\"\n",
    "        eta: 学习率\n",
    "        n_iter: 权重向量的训练次数\n",
    "        w_: 神经分叉权重向量\n",
    "        errors_: 用于记录神经元判断出错次数\n",
    "        \"\"\"\n",
    "        self.eta = eta\n",
    "        self.n_iter = n_iter\n",
    "        self.w_ = None\n",
    "        self.errors_ = None\n",
    "        pass\n",
    "\n",
    "    def fit(self, X, y):\n",
    "        \"\"\"\n",
    "        输入训练数据，培训神经元，X输入样本向量，y对应样本分类\n",
    "        X:shape[n_samples, n_features]\n",
    "        X:[[1,2,3],[4,5,6]]\n",
    "        n_samples: 2\n",
    "        n_features: 3\n",
    "        y:[1,-1]\n",
    "\n",
    "        初始化权重向量为0\n",
    "        加一是因为前面算法提到的w0，也就是步调函数阀值\n",
    "        \"\"\"\n",
    "        self.w_ = np.zeros(1 + X.shape[1])\n",
    "        self.errors_ = []\n",
    "        for _ in range(self.n_iter):\n",
    "            errors = 0\n",
    "            \"\"\"\n",
    "            X:[[1,2,3],[4,5,6]]\n",
    "            y:[1,-1]\n",
    "            zip(X,y) = [[1,2,3, 1],[4,5,6 -1]]\n",
    "            \"\"\"\n",
    "            for xi, target in zip(X, y):\n",
    "                update = self.eta * (target - self.predict(xi))\n",
    "                # xi是个向量\n",
    "                self.w_[1:] += update * xi\n",
    "                self.w_[0] += update\n",
    "\n",
    "                errors += int(update != 0.0)\n",
    "                self.errors_.append(errors)\n",
    "                pass\n",
    "        print(self.w_)\n",
    "        print(self.errors_)\n",
    "        pass\n",
    "\n",
    "    def net_input(self, X):\n",
    "        # z = W0*1 + W1*X1 + ... Wn*Xn\n",
    "        return np.dot(X, self.w_[1:] + self.w_[0])\n",
    "        pass\n",
    "\n",
    "    def predict(self, X):\n",
    "        return np.where(self.net_input(X) >= 0.0, 1, -1)\n",
    "        pass\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    },
    "pycharm": {
     "name": "#%%\n",
     "is_executing": true
    }
   },
   "outputs": [],
   "source": [
    "file = \"pythondata_iris.data.csv\"\n",
    "import pandas as pd\n",
    "\n",
    "df = pd.read_csv(file, header=None)\n",
    "df.head(10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "# rc方法，其实和设置rcParams 基本等效\n",
    "# 设置font字典为 SimSun（宋体），大小为12（默认为10）\n",
    "font = {'family': 'Arial Unicode MS',\n",
    "        'size': '12'}\n",
    "# 设置 字体\n",
    "plt.rc('font', **font)\n",
    "# 解决中文字体下坐标轴负数的负号显示问题\n",
    "plt.rc('axes', unicode_minus=False)\n",
    "\n",
    "y = df.iloc[0:100, 4].values\n",
    "print(y)\n",
    "y = np.where(y == 'Iris-setosa', -1, 1)\n",
    "print(y)\n",
    "X = df.iloc[0:100, [0, 2]].values\n",
    "print(X)\n",
    "plt.scatter(X[:50, 0], X[:50, 1], color='red', marker='o', label='setosa')\n",
    "plt.scatter(X[50:100, 0], X[50:100, 1], color='blue', marker='x', label='versicolor')\n",
    "plt.xlabel('花瓣长度')\n",
    "plt.ylabel('花茎长度')\n",
    "plt.legend(loc='upper left')\n",
    "plt.show()"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n",
     "is_executing": true
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [
    "ppn = Perceptron(eta=0.1, n_iter=10)\n",
    "ppn.fit(X, y)\n",
    "plt.plot(range(1, len(ppn.errors_) + 1), ppn.errors_, marker='o')\n",
    "plt.xlabel('Epochs')\n",
    "plt.ylabel('错误分类次数')\n",
    "plt.show()"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n",
     "is_executing": true
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "outputs": [],
   "source": [
    "from matplotlib.colors import ListedColormap\n",
    "\n",
    "\n",
    "def plot_decision_regions(X, y, classifier, resolution=0.02):\n",
    "    markers = ('s', 'x', 'o', 'v')\n",
    "    colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan')\n",
    "    cmap = ListedColormap(colors[:len(np.unique(y))])\n",
    "    x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max()\n",
    "    x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max()\n",
    "    # print(x1_min, x1_max)\n",
    "    # print(x2_min, x2_max)\n",
    "\n",
    "    xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution), np.arange(x2_min, x2_max, resolution))\n",
    "    # print(np.arange(x1_min, x1_max, resolution).shape)\n",
    "    # print(np.arange(x1_min, x1_max, resolution))\n",
    "    # print(xx1.shape)\n",
    "    # print(xx1, xx2)\n",
    "\n",
    "    Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)\n",
    "    print(xx1.ravel())\n",
    "    print(xx2.ravel())\n",
    "    print(Z)\n",
    "\n",
    "    Z = Z.reshape(xx1.shape)\n",
    "    plt.contourf(xx1, xx2, Z, alpha=0.4, cmap=cmap)\n",
    "    plt.xlim(xx1.min(), xx1.max())\n",
    "    plt.ylim(xx2.min(), xx2.max())\n",
    "\n",
    "    for idx, cl in enumerate(np.unique(y)):\n",
    "        plt.scatter(x=X[y == cl, 0], y=X[y == cl, 1], alpha=0.8, c=cmap(idx), marker=markers[idx], label=cl)"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "*c* argument looks like a single numeric RGB or RGBA sequence, which should be avoided as value-mapping will have precedence in case its length matches with *x* & *y*.  Please use the *color* keyword-argument or provide a 2D array with a single row if you intend to specify the same RGB or RGBA value for all points.\n",
      "*c* argument looks like a single numeric RGB or RGBA sequence, which should be avoided as value-mapping will have precedence in case its length matches with *x* & *y*.  Please use the *color* keyword-argument or provide a 2D array with a single row if you intend to specify the same RGB or RGBA value for all points.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[3.3  3.32 3.34 ... 6.94 6.96 6.98]\n",
      "[0.   0.   0.   ... 5.08 5.08 5.08]\n",
      "[-1 -1 -1 ...  1  1  1]\n"
     ]
    },
    {
     "data": {
      "text/plain": "<Figure size 432x288 with 1 Axes>",
      "image/png": "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\n"
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_decision_regions(X, y, ppn, resolution=0.02)\n",
    "plt.xlabel('花茎长度')\n",
    "plt.ylabel('花瓣长度')\n",
    "plt.legend(loc='upper left')\n",
    "plt.show()"
   ],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "outputs": [],
   "source": [],
   "metadata": {
    "collapsed": false,
    "pycharm": {
     "name": "#%%\n"
    }
   }
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}